package mandelbrot;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

import result.Result;
import tasks.Task;

public class MandelbrotTask extends Task {

	private int by;
	private int bx;
	private int boxWidth;
	private int boxHeight;
	private double zoom;
	private int width;
	private int height;
	private double viewX;
	private double viewY;
	private BufferedImage image;
	private int pal;
	private Color[][] colors;
	private boolean smooth = false;
	private int maxCount;
	private byte[] result;
	private long initTime;
	private long startTime;

	public MandelbrotTask(int id, int by, int bx, int boxWidth, int boxHeight,
			double zoom, int width, int height, double viewX, double viewY,
			Color[][] colors, int maxCount, long time) {
		super(id);
		this.by = by;
		this.bx = bx;
		this.boxWidth = boxWidth;
		this.boxHeight = boxHeight;
		this.zoom = zoom;
		this.width = width;
		this.height = height;
		this.viewX = viewX;
		this.viewY = viewY;
		this.colors = colors;
		this.maxCount = maxCount;
		this.initTime = time;
	}

	@Override
	public void run() {
		try {
			startTime=System.currentTimeMillis();
			
			image = new BufferedImage(boxWidth, boxHeight,
					BufferedImage.TYPE_INT_RGB);
			int offY = by * boxHeight;
			int offX = bx * boxWidth;
			for (int y = offY; y < (by + 1) * boxHeight; y++) {
				for (int x = offX; x < (bx + 1) * boxWidth; x++) {

					double r = zoom / (double) Math.min(width, height);
					double dx = 2.5 * (x * r + viewX) - 2;
					double dy = 1.25 - 2.5 * (y * r + viewY);
					Color color = color(dx, dy);
					image.setRGB(x - offX, y - offY, color.getRGB());
				}
			}
			ByteArrayOutputStream stream = new ByteArrayOutputStream(200000);

			ImageIO.write(image, "jpg", stream);

			stream.flush();
			stream.close();
			result = stream.toByteArray();
			
			startTime = System.currentTimeMillis() - startTime;

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public Result getResult() {
		return new MandelbrotResult(super.id,bx, by, result,startTime,initTime);
	}

	@Override
	public void cancel() {
		// TODO Auto-generated method stub

	}

	// Computes a color for a given point
	private Color color(double x, double y) {
		int count = mandel(0.0, 0.0, x, y);
		int palSize = colors[pal].length;
		Color color = colors[pal][count / 256 % palSize];
		if (smooth) {
			Color color2 = colors[pal][(count / 256 + palSize - 1) % palSize];
			int k1 = count % 256;
			int k2 = 255 - k1;
			int red = (k1 * color.getRed() + k2 * color2.getRed()) / 255;
			int green = (k1 * color.getGreen() + k2 * color2.getGreen()) / 255;
			int blue = (k1 * color.getBlue() + k2 * color2.getBlue()) / 255;
			color = new Color(red, green, blue);
		}
		return color;
	}

	// Computes a value for a given complex number
	private int mandel(double zRe, double zIm, double pRe, double pIm) {
		double zRe2 = zRe * zRe;
		double zIm2 = zIm * zIm;
		double zM2 = 0.0;
		int count = 0;
		while (zRe2 + zIm2 < 4.0 && count < maxCount) {
			zM2 = zRe2 + zIm2;
			zIm = 2.0 * zRe * zIm + pIm;
			zRe = zRe2 - zIm2 + pRe;
			zRe2 = zRe * zRe;
			zIm2 = zIm * zIm;
			count++;
		}
		if (count == 0 || count == maxCount)
			return 0;
		// transition smoothing
		zM2 += 0.000000001;
		return 256
				* count
				+ (int) (255.0 * Math.log(4 / zM2) / Math.log((zRe2 + zIm2)
						/ zM2));
	}

}
